﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace OnlineScan
{
    public partial class Form1 : Form
    {
        #region DllImport

        [DllImport("Iphlpapi.dll")]
        private static extern int SendARP(Int32 dest, Int32 host, ref Int64 mac, ref Int32 length);

        [DllImport("Ws2_32.dll")]
        private static extern Int32 inet_addr(string ip);

        #endregion

        #region Attributes

        private static Form myForm = new Form();
        private static long StartIP = 0;
        private static long EndIP = 0;
        private static long IPCount = 0;
        private static long CurrentIP = 0;

        private static Thread[] ScanThread;
        private static int Success = 0;
        private static int Fail = 0;
        private static bool IsPause = false;
        private static bool IsRunning = false;
        private delegate void DeleGateListView(ScanItem Item);
        private delegate void DeleGatebtnPause(bool b);
        private delegate void DeleGatebtnAbort(bool b);
        ManualResetEvent mre = new ManualResetEvent(true);

        public struct ScanItem
        {
            public string ip;
            public string status;
            public string mac;
            public string hostname;
        }

        #endregion

        #region Form

        public Form1()
        {
            InitializeComponent();
            SetBtnStart();
            InitListView();
        }

        private void Form1_SizeChanged(object sender, EventArgs e)
        {
            if (this.WindowState == FormWindowState.Minimized)
            {
                this.notifyIcon1.Visible = true;
                this.Hide();
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = true;

            if (IsRunning)
            {
                if (MessageBox.Show("正在进行扫描，要强制退出吗?", "系统提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
                {
                    try
                    {
                        for (int i = 0; i < GlobalAttrib.ThreadCount; i++)
                        {
                            ScanThread[i].Abort();
                        }
                    }
                    catch
                    {
                    }
                }
                else
                {
                    return;
                }
            }

            e.Cancel = false;
        }

        #endregion

        #region ToolStripMenuItem

        private void ToolStripMenuItem_Main_Click(object sender, EventArgs e)
        {
            this.Visible = true;
            this.WindowState = FormWindowState.Normal;
        }

        private void ToolStripMenuItem_About_Click(object sender, EventArgs e)
        {
            MessageBox.Show("QinTime.co.Ltd 2012", "版权信息", MessageBoxButtons.OK, MessageBoxIcon.None);
        }

        private void ToolStripMenuItem_Exit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void ToolStripMenuItem_SaveResult_Click(object sender, EventArgs e)
        {
            this.saveFileDialog1.ValidateNames = true;
            this.saveFileDialog1.Filter = "日志文件(*.log)|*.log|Excel文件(*.xls)|*.xls";
            this.saveFileDialog1.FilterIndex = 1;
            this.saveFileDialog1.DefaultExt = "log";
            this.saveFileDialog1.FileName = "";
            this.saveFileDialog1.AddExtension = true;
            if (this.saveFileDialog1.ShowDialog() == DialogResult.Cancel)
            {
                return;
            }

            if (this.saveFileDialog1.FileName.EndsWith(".xls"))
            {
                if (ListView2Excel(this.lvResult, this.saveFileDialog1.FileName))
                {
                    MessageBox.Show("导出成功！");
                }
                else
                {
                    MessageBox.Show("导出失败！");
                }
            }
            else
            {
                if (ListView2Text(this.lvResult, this.saveFileDialog1.FileName))
                {
                    MessageBox.Show("导出成功！");
                }
                else
                {
                    MessageBox.Show("导出失败！");
                }
            }
        }

        #endregion

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (!InitAttrib()) return;

            if (!IsRunning)
            {
                EntryScan();
                IsRunning = true;
                IsPause = false;
                this.btnStart.Text = "终止";
                this.btnPause.Text = "暂停";
                SetControl(false);
                this.btnPause.Enabled = true;
            }
            else
            {
                for (int i = 0; i < GlobalAttrib.ThreadCount; i++)
                {
                    try
                    {
                        ScanThread[i].Abort();
                    }
                    catch
                    {
                    }
                }
                IsRunning = false;
                this.btnStart.Text = "开始";
                SetControl(true);
                this.btnPause.Enabled = false;
            }
        }

        private void btnPause_Click(object sender, EventArgs e)
        {
            this.btnStart.Enabled = false;
            if (IsRunning)
            {
                if (IsPause)
                {
                    mre.Set();
                    IsPause = false;
                    this.btnPause.Text = "暂停";

                }
                else
                {
                    mre.Reset();
                    IsPause = true;
                    this.btnPause.Text = "恢复";
                }
            }
            this.btnStart.Enabled = true;
        }

        private bool InitAttrib()
        {
            if (this.tbIPStart.Text.Length == 0 && this.tbIPEnd.Text.Length == 0)
            {
                MessageBox.Show("请输入需要扫描的IP");
                return false;
            }
            if (this.tbIPStart.Text.Length == 0) this.tbIPStart.Text = this.tbIPEnd.Text;
            if (this.tbIPEnd.Text.Length == 0) this.tbIPEnd.Text = this.tbIPStart.Text;
            if ((!QT.Net.NetWork.CheckIP(this.tbIPStart.Text)) || (!QT.Net.NetWork.CheckIP(this.tbIPEnd.Text)))
            {
                MessageBox.Show("请输入合法的IP地址");
                return false;
            }
            if (IPToLong(this.tbIPStart.Text) > IPToLong(this.tbIPEnd.Text))
            {
                MessageBox.Show("结束IP必须等于或大于开始IP");
                return false;
            }

            GlobalAttrib.StartIP = this.tbIPStart.Text;
            GlobalAttrib.EndIP = this.tbIPEnd.Text;
            GlobalAttrib.ThreadCount = (int)this.nudThreadCount.Value;

            return true;
        }

        private void EntryScan()
        {
            this.lvResult.Clear();
            InitListView();

            StartIP = IPToLong(GlobalAttrib.StartIP);
            EndIP = IPToLong(GlobalAttrib.EndIP);
            CurrentIP = StartIP;
            Success = 0;
            Fail = 0;
            IsRunning = true;
            IPCount = EndIP - StartIP + 1;
            if (IPCount < GlobalAttrib.ThreadCount) GlobalAttrib.ThreadCount = IPCount;
            ScanThread = new Thread[GlobalAttrib.ThreadCount];

            mre.Set();
            for (long i = 0; i < GlobalAttrib.ThreadCount; i++)
            {
                ScanThread[i] = new Thread(new ThreadStart(NetScan));
                ScanThread[i].IsBackground = true;
                ScanThread[i].Start();
            }
        }

        private void NetScan()
        {
            SetControl(false);

            ScanItem Item;

            long ip = GetIP();
            while (ip != 0)
            {
                try
                {
                    Item = ApiGetMac(LongToIP(ip));
                    mre.WaitOne();
                    this.lvResult.Invoke(new DeleGateListView(AddItem), new object[] { Item });
                    if (Item.mac == "") Fail++;
                    else Success++;
                    SetTsslStatus(Success, Fail, LongToIP(ip));
                 }
                catch (ThreadAbortException)
                {
                }
                catch (InvalidOperationException)
                {
                }
                catch (Exception ex)
                {
                    MessageBox.Show("系统错误:" + ex.Message);
                    return;
                }

                ip = GetIP();
            }

            if (GetThreadCount() == 0)
            {
                IsRunning = false;
                SetControl(true);
                SetBtnStart();
            }
        }

        private void SetBtnStart()
        {
            if (IsRunning)
            {
                this.btnStart.Text = "终止";
                this.btnPause.Text = "暂停";
                SetControl(false);
                this.btnPause.Enabled = true;
            }
            else
            {
                this.btnStart.Text = "开始";
                SetControl(true);
                this.btnPause.Enabled = false;
            }
        }

        private void SetBtnPause()
        {
            if (IsRunning)
            {
                if (IsPause)
                {
                    IsPause = false;
                    this.btnPause.Text = "暂停";
                    this.tsslStatus.Text = "";

                }
                else
                {
                    IsPause = true;
                    this.btnPause.Text = "恢复";
                    this.tsslStatus.Text = "已暂停";
                }
            }
        }

        private void SetTsslStatus()
        {
            if (!IsRunning)
            {
                this.tsslStatus.Text = "已终止";
            }
            else
            {
                if (IsPause) this.tsslStatus.Text = "已暂停";
            }
        }

        private void SetTsslStatus(int Success, int Fail, string ip)
        {
            lock (this)
            {
                if (Success + Fail < IPCount) this.tsslStatus.Text = "在线：" + Success + "   离线：" + Fail + "    当前：" + ip;
                else this.tsslStatus.Text = "在线：" + Success + "   离线：" + Fail + "   扫描已完成";
            }
        }

        private void SetControl(bool b)
        {
            this.ToolStripMenuItem_SaveResult.Enabled = b;
        }

        private void InitListView()
        {
            this.lvResult.View = View.Details;
            this.lvResult.BeginUpdate();
            this.lvResult.FullRowSelect = true;
            this.lvResult.Columns.Add("序号", 40, HorizontalAlignment.Center);
            this.lvResult.Columns.Add("IP地址", 140, HorizontalAlignment.Center);
            this.lvResult.Columns.Add("状态", 70, HorizontalAlignment.Center);
            this.lvResult.Columns.Add("MAC地址", 140, HorizontalAlignment.Center);
            this.lvResult.Columns.Add("主机名", 140, HorizontalAlignment.Center);
            this.lvResult.GridLines = true;
            this.lvResult.EndUpdate();
        }

        private void AddItem(ScanItem Item)
        {
            lock (this)
            {
                bool IsComplete = false;

                this.lvResult.BeginUpdate();
                ListViewItem newItem = new ListViewItem();
                newItem.SubItems[0].Text = "";
                newItem.SubItems.Add(Item.ip);
                newItem.SubItems.Add(Item.status);
                newItem.SubItems.Add(Item.mac);
                newItem.SubItems.Add(Item.hostname);

                for (int i = 0; i < this.lvResult.Items.Count; i++)
                {
                    if (!IsComplete && (QT.Net.NetWork.Compare(this.lvResult.Items[i].SubItems[1].Text, Item.ip) > 0))
                    {
                        this.lvResult.Items.Insert(i, newItem);
                        IsComplete = true;
                    }

                    this.lvResult.Items[i].SubItems[0].Text = (i + 1).ToString();
                }

                if (!IsComplete)
                {
                    this.lvResult.Items.Add(newItem);
                    this.lvResult.Items[this.lvResult.Items.Count - 1].SubItems[0].Text = this.lvResult.Items.Count.ToString();
                }

                this.lvResult.EndUpdate();
            }
        }

        private ScanItem ApiGetMac(string ip)
        {
            ScanItem newItem = new ScanItem();
            newItem.ip = ip;

            Int64 macinfo = new Int64();
            Int32 length = 6;
            string mac = "";
            try
            {
                SendARP(inet_addr(ip), 0, ref macinfo, ref length);
                if (macinfo == 0) newItem.mac = "";
                mac = macinfo.ToString("X12");
                if (mac != "0000" && mac != "000000000000" && mac.Length == 12)
                {
                    newItem.mac = new string(new char[] { mac[10], mac[11], '-', mac[8], mac[9], '-', mac[6], mac[7], '-', mac[4], mac[5], '-', mac[2], mac[3], '-', mac[0], mac[1] });
                    newItem.status = "在线";
                }
                else
                {
                    newItem.mac = "";
                    newItem.status = "离线";
                }
            }
            catch
            {
                newItem.mac = "";
                newItem.status = "离线";
                Fail++;
            }

            try
            {
                IPHostEntry Host = Dns.GetHostEntry(ip);
                if (Host.HostName != ip) newItem.hostname = Host.HostName;
                else newItem.hostname = "";
            }
            catch
            {
            }

            return newItem;
        }

        public long GetIP()
        {
            long ip = 0;
            lock (this)
            {
                if (CurrentIP <= EndIP)
                {
                    ip = CurrentIP;
                    CurrentIP++;
                }
                else
                {
                    IsRunning = false;
                }
            }
            return ip;
        }

        public long GetThreadCount()
        {
            long i = 0;
            lock (this)
            {
                GlobalAttrib.ThreadCount--;
                i = GlobalAttrib.ThreadCount;
            }
            return i;
        }

        public static long IPToLong(string ip_Long)
        {
            char[] separator = new char[] { '.' };
            if (ip_Long.Split(separator).Length == 3)
            {
                ip_Long = ip_Long + ".0";
            }
            string[] strArray = ip_Long.Split(separator);
            long num2 = ((long.Parse(strArray[0]) * 0x100L) * 0x100L) * 0x100L;
            long num3 = (long.Parse(strArray[1]) * 0x100L) * 0x100L;
            long num4 = long.Parse(strArray[2]) * 0x100L;
            long num5 = long.Parse(strArray[3]);
            return (((num2 + num3) + num4) + num5);
        }

        public static string LongToIP(long ip_Int)
        {
            long num = (long)((ip_Int & 0xff000000L) >> 0x18);
            if (num < 0L)
            {
                num += 0x100L;
            }
            long num2 = (ip_Int & 0xff0000L) >> 0x10;
            if (num2 < 0L)
            {
                num2 += 0x100L;
            }
            long num3 = (ip_Int & 0xff00L) >> 8;
            if (num3 < 0L)
            {
                num3 += 0x100L;
            }
            long num4 = ip_Int & 0xffL;
            if (num4 < 0L)
            {
                num4 += 0x100L;
            }
            return (num.ToString() + "." + num2.ToString() + "." + num3.ToString() + "." + num4.ToString());
        }

        private bool ListView2Excel(ListView listView, string strFileName)
        {
            int rowNum = listView.Items.Count;
            int columnNum = listView.Items[0].SubItems.Count;
            int rowIndex = 1;
            int columnIndex = 0;
            if (rowNum == 0 || string.IsNullOrEmpty(strFileName))
            {
                return false;
            }
            if (rowNum > 0)
            {

                Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.ApplicationClass();
                if (xlApp == null)
                {
                    MessageBox.Show("无法创建excel对象，可能您的系统没有安装excel");
                    return false;
                }
                xlApp.DefaultFilePath = "";
                xlApp.DisplayAlerts = true;
                xlApp.SheetsInNewWorkbook = 1;
                Microsoft.Office.Interop.Excel.Workbook xlBook = xlApp.Workbooks.Add(true);
                //将ListView的列名导入Excel表第一行
                foreach (ColumnHeader dc in listView.Columns)
                {
                    columnIndex++;
                    xlApp.Cells[rowIndex, columnIndex] = dc.Text;
                }
                //将ListView中的数据导入Excel中
                for (int i = 0; i < rowNum; i++)
                {
                    rowIndex++;
                    columnIndex = 0;
                    for (int j = 0; j < columnNum; j++)
                    {
                        columnIndex++;
                        //注意这个在导出的时候加了“\t” 的目的就是避免导出的数据显示为科学计数法。可以放在每行的首尾。
                        xlApp.Cells[rowIndex, columnIndex] = Convert.ToString(listView.Items[i].SubItems[j].Text) + "\t";
                    }
                }
                //例外需要说明的是用strFileName,Excel.XlFileFormat.xlExcel9795保存方式时 当你的Excel版本不是95、97 而是2003、2007 时导出的时候会报一个错误：异常来自 HRESULT:0x800A03EC。 解决办法就是换成strFileName, Microsoft.Office.Interop.Excel.XlFileFormat.xlWorkbookNormal。
                xlBook.SaveAs(strFileName, Microsoft.Office.Interop.Excel.XlFileFormat.xlWorkbookNormal, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlExclusive, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
                xlBook = null; 
                xlApp = null;                
            }
            return true;
        }

        private bool ListView2Text(ListView listView, string strFileName)
        {
            FileStream fs = new FileStream(strFileName, FileMode.Append, FileAccess.Write, FileShare.Read);
            StreamWriter text = new StreamWriter(fs, Encoding.Default);
            string RecordLine = "";

            try
            {
                for (int i = 0; i < listView.Columns.Count; i++)
                {
                    RecordLine += listView.Columns[i].Text + "\t";
                }
                text.WriteLine(RecordLine);

                for (int i = 0; i < listView.Items.Count; i++)
                {
                    RecordLine = "";
                    for (int j = 0; j < listView.Columns.Count; j++)
                    {
                        RecordLine += listView.Items[i].SubItems[j].Text + "\t";
                    }
                    text.WriteLine(RecordLine);
                }
            }
            catch
            {
                return false;
            }

            text.Close();
            text.Dispose();
            fs.Close();
            fs.Dispose();
            return true;
        }
    }

    public class CustomListView : ListView
    {
        public CustomListView()
        {
            SetStyle(ControlStyles.DoubleBuffer | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
            UpdateStyles();
        }
    }
}
